#' Read and Preprocess LI-6800 Induction Curve Data
#'
#' This function reads an Excel file containing induction curve data from a
#' LI-6800 instrument, filters for relevant data points, performs
#' standardization, converts time units, and assigns a custom class `jip` to
#' the resulting data frame for easy plotting and analysis.
#'
#' @param path A character string. The full path or URL to the LI-6800 INDUCTION
#'   Excel file.
#' @param code_filter A numeric vector. The values in the `CODE` column that
#'   should be retained. Defaults to `3:6`, which are typically the codes
#'   corresponding to the light-induced fluorescence transient.
#'
#' @return A data frame of class `jip` containing the processed data. The
#'   data frame includes the following columns:
#'   \itemize{
#'     \item `EVENT_ID`, `TRACE_NO`, `SECS`, `FLUOR`, `DC`, `PFD`, `REDMODAVG`, `CODE`: Original columns from the Excel file.
#'     \item `SOURCE`: The filename (without extension) from which the data was read.
#'     \item `NORM_FLUOR`: The `FLUOR` values normalized to the range 0-1
#'     \item `NORM_DC`: The `DC` values normalized to the range 0-1
#'     \item `MILLI_SEC`: The time in milliseconds, converted from `SECS`.
#'   }
#'
#' @details
#' The function executes the following steps:
#' 1.  **Read File**: Uses `openxlsx2::read_xlsx` to read the Excel file.
#' 2.  **Add Source**: Creates a `SOURCE` column with the filename (without extension).
#' 3.  **Validate Columns**: Checks for the presence of essential columns (`CODE`, `SECS`, `FLUOR`, `DC`).
#' 4.  **Filter Rows**: Retains only rows where `CODE` is in `code_filter`. Issues a warning if no rows match.
#' 5.  **Normalize Signals**: Computes `NORM_FLUOR` and `NORM_DC` by scaling the signals to 0-1.
#' 6.  **Convert Time**: Converts time from seconds (`SECS`) to milliseconds (`MILLI_SEC`).
#' 7.  **Set Class**: Assigns the `jip` class to the data frame to enable specialized methods (e.g., `plot.jip`).
#'
#' @examples
#' \dontrun{
#' library(jiptest)
#' # Read a single induction curve file
#' jip_data = read_induction("path/to/your/induction_file.xlsx")
#'
#' # Check the structure
#' str(jip_data)
#'
#' # Since it has class 'jip', a custom plot function can be used
#' plot(jip_data)
#' }
#'
#' @export

read_induction = function(path, code_filter = 3:6) {
  # Check if the file exists
  if (!file.exists(path)) {
    stop("The file '", path, "' does not exist.")
  }
  
  # Read the Excel file
  df = openxlsx2::read_xlsx(path)
  
  # Add SOURCE column (filename without extension)
  df$SOURCE = tools::file_path_sans_ext(basename(path))
  
  # Define required columns
  required_cols = c("CODE", "SECS", "FLUOR", "DC")
  if (!all(required_cols %in% colnames(df))) {
    missing_cols = setdiff(required_cols, colnames(df))
    stop("The file is missing required columns: ", paste(missing_cols, collapse = ", "))
  }
  
  # Filter rows based on CODE
  df_filtered = df[df$CODE %in% code_filter, , drop = FALSE]
  
  if (nrow(df_filtered) == 0) {
    warning("No rows remaining after filtering with code_filter = c(", 
            paste(code_filter, collapse = ", "), "). Returning empty data frame.")
    # Assign class and return to maintain consistency
    class(df_filtered) = c("jip", class(df_filtered))
    return(df_filtered)
  }
  
  # Select and reorder columns (optional but makes output consistent)
  keep_columns = c('EVENT_ID', 'TRACE_NO', 'SECS', 'FLUOR', 'DC', 'PFD', 'REDMODAVG', 'CODE', 'SOURCE')
  # Only keep columns that exist in df_filtered
  keep_columns = intersect(keep_columns, colnames(df_filtered))
  df_processed = df_filtered[, keep_columns]
  
  # Normalize FLUOR and DC signals, with check for division by zero
  range_fluor = range(df_processed$FLUOR, na.rm = TRUE)
  df_processed$NORM_FLUOR = if (diff(range_fluor) == 0) {
    warning("All FLUOR values are identical. NORM_FLUOR set to 0.")
    0
  } else {
    (df_processed$FLUOR - range_fluor[1]) / diff(range_fluor)
  }
  
  range_dc = range(df_processed$DC, na.rm = TRUE)
  df_processed$NORM_DC = if (diff(range_dc) == 0) {
    warning("All DC values are identical. NORM_DC set to 0.")
    0
  } else {
    (df_processed$DC - range_dc[1]) / diff(range_dc)
  }
  
  # Convert time from seconds to milliseconds
  df_processed$MILLI_SEC = df_processed$SECS * 1000
  
  # Assign the 'jip' class
  class(df_processed) = c("jip", class(df_processed))
  
  return(df_processed)
}

